require 'rake'
require 'json'

require_relative 'common'
require_relative 'unity_helper'
require_relative 'unity_project'
require_relative 'android_aar_build_result'
require_relative 'platform'

require 'cucumber/rake/task'

include Common

Cucumber::Rake::Task.new do |t|
  t.cucumber_opts = '--format pretty'
end

namespace :lunar do

  task :init do

    $dir_out = File.expand_path 'temp'
    $dir_out_packages = "#{$dir_out}/packages"

    $dir_builder = File.expand_path '.'
    $dir_utils = resolve_path "#{$dir_builder}/utils"

    $dir_repo = resolve_path File.expand_path('..')

    # unity
    $bin_unity_export = resolve_path Platform.unity_export
    $bin_unity_publish = resolve_path Platform.unity_publish

    # plugin project
    $dir_project = resolve_path File.expand_path('../Project')
    $dir_project_plugin = resolve_path "#{$dir_project}/Assets/LunarConsole"
    $dir_project_plugin_scripts = resolve_path "#{$dir_project_plugin}/Scripts"
    $dir_project_plugin_editor = resolve_path "#{$dir_project_plugin}/Editor"
    $dir_project_plugin_ios = "#{$dir_project_plugin_editor}/iOS"
    $dir_project_plugin_android = "#{$dir_project_plugin_editor}/Android"

    # native projects
    $dir_native = resolve_path File.expand_path('../Native')
    $dir_native_ios = resolve_path "#{$dir_native}/iOS"
    $dir_native_ios_src = resolve_path "#{$dir_native_ios}/LunarConsole/LunarConsole"
    $dir_native_android = resolve_path "#{$dir_native}/Android"
    $dir_native_android_src = resolve_path "#{$dir_native_android}/LunarConsole"

    $dir_native_ios_images_root = resolve_path $dir_native_ios_src
    $dir_native_android_images_root = resolve_path "#{$dir_native_android_src}/lunarConsole/src/main/res"

    # test project
    $dir_test_project = resolve_path_e 'TestProject'

    def extract_package_version(dir_project)

      file_version = resolve_path "#{dir_project}/Scripts/Constants.cs"
      source = File.read file_version

      source =~ /Version\s+=\s+"(\d+\.\d+.\d+\w?)"/
      return not_nil $1

    end

    $package_version = not_nil(extract_package_version $dir_project_plugin)

  end

  desc 'Cleans temporary folders'
  task :clean => [:init] do
    FileUtils.rm_rf $dir_out
    FileUtils.makedirs $dir_out
  end

  task :full => [:init] do
    $package_config = 'Full'
  end

  task :free => [:init] do
    $package_config = 'Free'
  end

  desc 'Build native part of full the plugin'
  task :build_native_full => [:full, :build_native] do
  end

  desc 'Build native part of free the plugin'
  task :build_native_free => [:free, :build_native] do
  end

  desc 'Build native part of full the plugin'
  task :build_native => [:build_native_ios, :build_native_android] do
  end

  desc 'Build native iOS part of the plugin'
  task :build_native_ios => [:init] do

    # list ios project files
    ios_files = list_ios_files $dir_native_ios_src, :configuration => $package_config, :use_relative_path => true

    # cleanup old files
    FileUtils.rm_rf $dir_project_plugin_ios
    FileUtils.mkdir $dir_project_plugin_ios

    # copy files to plugin native folder (keeping directory structure)
    ios_files.each {|file|
      file_src = "#{$dir_native_ios_src}/#{file}"
      dir_dest = "#{$dir_project_plugin_ios}/#{File.dirname file}"
      FileUtils.mkpath dir_dest unless File.directory? dir_dest

      FileUtils.cp file_src, "#{dir_dest}/"
    }

    # generate .projmod
    projmods = {
        group: 'Lunar Console',
        frameworks: ['MessageUI.framework'],
        files: ios_files,
        excludes: %w(^.*.DS_Store$ ^.*.meta$ ^.*.mdown^ ^.*.pdf$ ^.*.svn$),
    }

    file_projmode = "#{$dir_project_plugin_ios}/Lunar.projmods"
    File.write file_projmode, JSON.pretty_generate(projmods)

  end

  desc 'Build native Android part of the plugin'
  task :build_native_android => [:init] do

    # cleanup old file
    FileUtils.rm_rf $dir_project_plugin_android
    FileUtils.mkpath $dir_project_plugin_android

    # build android library
    build_android_plugin $dir_native_android_src, 'lunarConsole', $package_config, $dir_project_plugin_android

  end

  task :list_package_files => [:init] do
    $package_files = UnityHelper.list_package_assets $dir_project_plugin

    print_header 'Package files:'
    puts $package_files
    # TODO: add readme file
  end

  desc 'Export unity packages'
  task :export_unity_packages => [:clean, :export_unity_package_free, :export_unity_package_full] do
  end

  desc 'Export full package'
  task :export_unity_package_full => [:full, :export_package] do
  end

  desc 'Export free package'
  task :export_unity_package_free => [:free, :export_package] do
  end

  task :export_package => [:init, :build_native, :list_package_files] do

    file_package = "#{$dir_out_packages}/lunar-console-#{$package_config.downcase}-#{$package_version}.unitypackage"
    print_header "Exporting package: #{file_package}..."

    override_configuration_define resolve_path("#{$dir_project_plugin}/Scripts/LunarConsole.cs"), $package_config

    package_files = not_nil $package_files

    project = UnityProject.new $dir_project, $bin_unity_export
    project.export_unity_package file_package, package_files

  end

  desc 'Build and run full native Android part'
  task :build_run_native_android_full => [:full, :build_run_native_android] do
  end

  desc 'Build and run free native Android part'
  task :build_run_native_android_free => [:free, :build_run_native_android] do
  end

  task :build_run_native_android => [:build_native_android] do

    project = UnityProject.new $dir_project, $bin_unity_export
    project.exec_unity_method 'LunarConsoleEditorInternal.AndroidPlugin.ForceUpdateFiles'
    project.exec_unity_method 'LunarConsoleEditorInternal.AppExporter.PerformAndroidBuild'

    file_app = resolve_path Dir["#{$dir_project}/Build/Android/*.apk"].first

    # print_header 'Stopping app...'
    # exec_shell 'adb shell am force-stop com.spacemadness.LunarConsole', "Can't stop app"

    print_header 'Installing app...'
    exec_shell %(adb install -r "#{file_app}"), "Can't install app..."

    print_header 'Starting app...'
    exec_shell 'adb shell am start -n com.spacemadness.LunarConsoleTest/com.unity3d.player.UnityPlayerActivity', "Can't start app"

  end

  desc 'Optimize png files'
  task :optimize_png_files => [:init] do

    files = []

    files.push *list_png_file($dir_native_ios_images_root)
    files.push *list_png_file($dir_native_android_images_root)

    bin_pngout = resolve_path "#{$dir_utils}/pngout"

    size_before = 0.0
    size_after = 0.0

    files.each { |file|
      size_before += File.size file
      exec_shell %("#{bin_pngout}" "#{file}" -c3 -y -force), "Can't optimize png: #{file}"
      size_after += File.size file
    }

    print_header "Compression rate: #{100 * (size_after/size_before)}% (#{size_after}/#{size_before})"

  end

  def list_png_file(dir_project)
    list_files dir_project, &->(file) {
      return false if File.directory? file

      extension = File.extname file
      return extension == '.png'
    }
  end

  def list_ios_files(dir_project, options = {})
    extensions = %w(.h .m .mm .xib .nib .c .cpp .png .bundle)
    configuration = options[:configuration]

    files = list_files dir_project, &->(file) {
      return false if File.directory? file

      return false if file.include?('/Full/') and configuration == 'Free'
      return false if file.include?('/Free/') and configuration == 'Full'

      extension = File.extname file
      return extensions.include? extension
    }

    return files.map { |file| make_relative_path file, dir_project } if options[:use_relative_path]

    return files
  end

  def build_android_plugin(dir_native_project, module_name, flavour, dir_android_plugin)

    # build library
    print_header 'Building Android library...'
    file_aar = build_android dir_native_project, module_name, flavour

    # prepare plugin
    print_header 'Preparing Android plugin'

    FileUtils.cp file_aar, "#{dir_android_plugin}/lunar-console.aar"
  end

  def build_android(dir_project, module_name, flavour, config = 'Release')

    Dir.chdir dir_project do
      exec_shell "./gradlew :#{module_name}:clean :#{module_name}:assemble#{flavour}#{config}", "Can't build Android aar"
      return resolve_path_e "#{module_name}/build/outputs/aar/#{module_name}-#{flavour.downcase}-#{config.downcase}.aar"

    end

  end

  def write_project_properties(file, properties)
    File.open(file, 'w') { |f|
      properties.each {|name, value|
        f.puts "#{name}=#{value}"
      }
    }
  end

  def override_configuration_define(file_script, configuration)
    source = File.read file_script
    if configuration == 'Full'
      source.gsub! '#define LUNAR_CONSOLE_FREE', '#define LUNAR_CONSOLE_FULL'
    elsif configuration == 'Free'
      source.gsub! '#define LUNAR_CONSOLE_FULL', '#define LUNAR_CONSOLE_FREE'
    else
      fail_script "Unexpected configuration: #{configuration}"
    end

    File.open(file_script, 'w') { |f| f.write source }
  end

end
